#ifndef IOMEM_H
#define IOMEM_H

#define DEVIO_SIZE8  (1 << 0)
#define DEVIO_SIZE16 (1 << 1)
#define DEVIO_SIZE32 (1 << 2)

#define DEVRAM_FLAG_ROM         (1 << 0)
#define DEVRAM_FLAG_DIRTY_BITS  (1 << 1)
#define DEVRAM_FLAG_DISABLED    (1 << 2)

#define DEVRAM_PAGE_SIZE_LOG2 12
#define DEVRAM_PAGE_SIZE    (1 << DEVRAM_PAGE_SIZE_LOG2)

typedef void DeviceWriteFunc (void *opaque, uint32_t offset,
                              uint32_t val, int size_log2);
typedef uint32_t DeviceReadFunc (void *opaque, uint32_t offset,
                                int size_log2);

typedef struct PhysMemoryMap PhysMemoryMap;

typedef struct {
    PhysMemoryMap *map;
    uint64_t addr;
    uint64_t size;
    BOOL is_ram;
    int devram_flags;
    int devio_flags;
    uint8_t *phys_mem;
    void *opaque; // VIRTIODevice
    DeviceReadFunc *read_func;
    DeviceWriteFunc *write_func;
} PhysMemoryRange;

#define PHYS_MEM_RANGE_MAX 32

struct PhysMemoryMap {
    int n_phys_mem_range;
    PhysMemoryRange phys_mem_range[PHYS_MEM_RANGE_MAX];
    PhysMemoryRange *(*register_ram) (PhysMemoryMap *s, uint64_t addr,
                                      uint64_t size, int devram_flags);
    PhysMemoryRange *(*register_device) (PhysMemoryMap *s, uint64_t addr,
                                        uint64_t size, void * opaque,
                                        DeviceReadFunc *read_func, DeviceWriteFunc *write_func,
                                        int devio_flags);
};

PhysMemoryMap * phys_mem_map_init ();

static inline PhysMemoryRange *iomem_register_ram (PhysMemoryMap *s, uint64_t addr,
                                      uint64_t size, int devram_flags) {
    return s->register_ram (s, addr, size, devram_flags);
}

static inline PhysMemoryRange *iomem_register_device (PhysMemoryMap *s, uint64_t addr,
                                        uint64_t size, void * opaque,
                                        DeviceReadFunc *read_func, DeviceWriteFunc *write_func,
                                        int devio_flags) {
    return s->register_device (s, addr, size, opaque, read_func, write_func, devio_flags);
}

PhysMemoryRange *iomem_get_phys_mem_range (PhysMemoryMap *s, uint64_t paddr);

uint8_t * iomem_get_phys_mem_ptr (PhysMemoryMap *s, uint64_t paddr);

typedef void SetIRQFunc (void *opaque, int irq_num, int level);

typedef struct {
    SetIRQFunc *set_irq;
    void *opaque;
    int irq_num;
} IRQSignal;

void irq_init (IRQSignal *irq, SetIRQFunc *set_irq, void *opaque, int irq_num);

static inline void set_irq (IRQSignal *irq, int level) {
    irq->set_irq (irq->opaque, irq->irq_num, level);
}

#endif